﻿using System;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Interactivity;
using Hopscotch.Presentation.Common.Adorners;
using Microsoft.Practices.Prism.Interactivity.InteractionRequest;

namespace Hopscotch.Presentation.Common.TriggerActions
{
    public class OverlayWindowAction : TriggerAction<FrameworkElement>
    {
        private OverlayAdorner overlayAdorner;

        public static readonly DependencyProperty WindowContentProperty =
            DependencyProperty.Register(
                nameof(WindowContent),
                typeof(FrameworkElement),
                typeof(OverlayWindowAction),
                new PropertyMetadata(new PropertyChangedCallback(OnOverlayContentChanged)));

        public static readonly DependencyProperty TargetElementProperty =
            DependencyProperty.Register(
                nameof(TargetElement),
                typeof(FrameworkElement),
                typeof(OverlayWindowAction),
                new PropertyMetadata(null));

        /// <summary>
        /// Gets or sets the content of the overlay.
        /// </summary>
        public FrameworkElement WindowContent
        {
            get { return (FrameworkElement)GetValue(WindowContentProperty); }
            set { SetValue(WindowContentProperty, value); }
        }

        /// <summary>
        /// Target element to attach the overlay
        /// </summary>
        public FrameworkElement TargetElement
        {
            get { return (FrameworkElement)GetValue(TargetElementProperty); }
            set { SetValue(TargetElementProperty, value); }
        }

        protected override void Invoke(object parameter)
        {
            // Check if the WindowContent is part of another visual tree.
            if (this.WindowContent != null && this.WindowContent.Parent != null)
            {
                return;
            }

            var args = parameter as InteractionRequestedEventArgs;

            AddOverlayContent(args);
        }

        private static void OnOverlayContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            //Todo
        }

        private void RemoveExistingOverlays(FrameworkElement frameworkElement)
        {
            AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(frameworkElement);
            Adorner[] toRemoveArray = adornerLayer?.GetAdorners(frameworkElement);
            if (toRemoveArray != null)
            {
                foreach (Adorner adorner in toRemoveArray)
                {
                    var overlayAdorner = adorner as OverlayAdorner;
                    if (overlayAdorner != null)
                    {
                        overlayAdorner.RemoveOverlay();
                    }
                }
            }
        }

        private void AddOverlayContent(InteractionRequestedEventArgs args)
        {
            if (this.TargetElement == null)
                return;

            RemoveExistingOverlays(this.TargetElement);

            if (this.WindowContent != null)
            {
                this.overlayAdorner = new OverlayAdorner(this.TargetElement, this.WindowContent);
                this.overlayAdorner.AddOverlay();

                if (args != null)
                {
                    // Invoke the callback when the interaction's overlay is closed.
                    var callback = args.Callback;
                    EventHandler handler = null;
                    handler = (o, e) =>
                    {
                        overlayAdorner.Closed -= handler;
                        if (callback != null)
                            callback();
                    };
                    overlayAdorner.Closed += handler;

                    if (args.Context != null)
                        overlayAdorner.DataContext = args.Context;

                    this.PrepareContentForOverlay(args.Context, overlayAdorner);
                }
            }
        }


        /// <summary>
        /// Checks if the WindowContent or its DataContext implements <see cref="IInteractionRequestAware"/>.
        /// If so, it sets the corresponding value.
        /// </summary>
        /// <param name="notification">The notification to be set as a DataContext in the HostWindow.</param>
        /// <param name="wrapperWindow">The HostWindow</param>
        protected virtual void PrepareContentForOverlay(INotification notification, OverlayAdorner overlayAdorner)
        {
            if (this.WindowContent == null)
            {
                return;
            }

            IInteractionRequestAware interactionAware;

            // If the WindowContent implements IInteractionRequestAware we set the corresponding properties.
            interactionAware = this.WindowContent as IInteractionRequestAware;
            if (interactionAware != null)
            {
                interactionAware.Notification = notification;
                interactionAware.FinishInteraction = () => overlayAdorner.RemoveOverlay();
            }

            // If the WindowContent's DataContext implements IInteractionRequestAware we set the corresponding properties.
            interactionAware = this.WindowContent.DataContext as IInteractionRequestAware;
            if (interactionAware != null)
            {
                interactionAware.Notification = notification;
                interactionAware.FinishInteraction = () => overlayAdorner.RemoveOverlay();
            }
        }
       
    }
}
